using System;
using System.IO;
using System.Text;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;


namespace EpisodeWizard.Core
{

	public class TagEngine
	{

		/// <summary>
		///  Regular expressions to identify tags and characters in a naming pattern
		/// </summary>
		const string RegexTag			= "\\<[a-zA-Z0-9]{1,15}\\>";
		const string RegexValidChars	= "[a-zA-Z0-9 \\.\\-_\\[\\]]";		// Letters, numbers, blank, and ._-[]
		const string FullRegex 			= "((" + RegexTag + ")|(" + RegexValidChars + "))";

		
		/// <summary>
		/// A naming tag
		/// Holds the episode property it retrieves and its description
		/// </summary>
		public class TagInfo
		{
			public TagInfo(EpisodeValueDelegate _Delegate, string _Description)
			{
				ValueDelegate = _Delegate;
				Description = _Description;
			}
			public string Description;
			public EpisodeValueDelegate ValueDelegate;
		}
		
		
		/// <summary>
		/// The available tags and the propery they are associated with
		/// </summary>
		public Dictionary<string, TagInfo>	Tags;


		public delegate string EpisodeValueDelegate(EpisodeFile EF);
		public string GetEpisodeShowName(EpisodeFile EF)
		{
			return EF.PossibleShows.SelectedValue.ShowName;
		}
		public string GetEpisodeSeasonNumberShort(EpisodeFile EF)
		{
			return EF.PossibleEpisodes.SelectedValue.Season.ToString();
		}
		public string GetEpisodeSeasonNumberLong(EpisodeFile EF)
		{
			return EF.PossibleEpisodes.SelectedValue.Season.ToString("00");
		}
		public string GetEpisodeEpisodeNumberShort(EpisodeFile EF)
		{
			return EF.PossibleEpisodes.SelectedValue.Episode.ToString();
		}
		public string GetEpisodeEpisodeNumberLong(EpisodeFile EF)
		{
			return EF.PossibleEpisodes.SelectedValue.Episode.ToString("00");
		}
		public string GetEpisodeTitle(EpisodeFile EF)
		{
			return EF.PossibleTitles.SelectedValue;
		}


		/// <summary>
		/// Creates the list of available tags
		/// </summary>
		public TagEngine()
		{
			Tags = new Dictionary<string, TagInfo>();
			Tags.Add("<show>",			new TagInfo(GetEpisodeShowName, "Name of the show"));
			Tags.Add("<seasonshort>",	new TagInfo(GetEpisodeSeasonNumberShort, "Season number (1 or 2 digits)"));
			Tags.Add("<seasonlong>",	new TagInfo(GetEpisodeSeasonNumberLong, "Season number (2 digits with leading 0)"));
			Tags.Add("<episodeshort>",	new TagInfo(GetEpisodeEpisodeNumberShort, "Episode number (1 or 2 digits)"));
			Tags.Add("<episodelong>",	new TagInfo(GetEpisodeEpisodeNumberLong, "Episode number (2 digits with leading 0)"));
			Tags.Add("<title>",			new TagInfo(GetEpisodeTitle, "Episode title"));
		}

		
		/// <summary>
		/// Returns true if the specified pattern is valid for renaming files
		/// A valid pattern has  valid tag syntax and only uses available tags,
		/// There are also some requirements such as the use of mandatory tags
		/// </summary>
		public bool CheckValidPattern(string Pattern)
		{
			return CheckFormat(Pattern) && CheckTags(Pattern) && CheckMandatoryTags(Pattern);
		}

		
		/// <summary>
		/// The pattern is only valid if the number of matches is exactly 1, and of the same length
		/// </summary>
		private bool CheckFormat(string Pattern)
		{
			Regex R = new Regex(FullRegex + "+");
			MatchCollection MatchedTags = R.Matches(Pattern);
			if (MatchedTags.Count == 1)
			{
				string MatchedString = MatchedTags[0].Groups[0].Value;
				return (MatchedString.Length == Pattern.Length);
			}
			else
			{
				return false;
			}
		}

		
		/// <summary>
		/// The pattern is only valid if each match is contained in the tag list
		/// </summary>
		private bool CheckTags(string Pattern)
		{
			Regex R = new Regex(RegexTag);
			MatchCollection MatchedTags = R.Matches(Pattern);
			foreach (Match M in MatchedTags)
			{
				string TagName = M.Groups[0].Value;
				if (Tags.ContainsKey(TagName) == false)
				{
					return false;
				}
			}
			return true;
		}
		
		
		/// <summary>
		/// The pattern has to use certain tags so that the new files name are unique
		/// For example "<show>" would not be enough as all the files would be named the same
		/// </summary>
		private bool CheckMandatoryTags(string Pattern)
		{
			return true;
		}

		
		/// <summary>
		/// Computes the new filename for all the episode files
		/// </summary>
		public void ComputeFilenames(string Pattern)
		{
			// This will return the tags and individual characters in the right order
			Regex R = new Regex(FullRegex);
			MatchCollection MatchedTags = R.Matches(Pattern);
			foreach (EpisodeFile EF in Program.GetInstance().EpisodeFiles)
			{
				if (EF.HasAllInformation())
				{
					ComputeFilename(EF, MatchedTags);
				}
			}
		}

		
		/// <summary>
		/// Computes the new filename of an episode file
		/// By replacing the tags by the corresponding values
		/// </summary>
		private void ComputeFilename(EpisodeFile EF, MatchCollection MatchedTags)
		{
			string NewName = "";
			
			foreach (Match M in MatchedTags)
			{
				string Capture = M.Groups[0].Value;
				if (Tags.ContainsKey(Capture))
				{
					TagInfo TI = Tags[Capture];
					NewName += TI.ValueDelegate(EF);
				}
				else
				{
					NewName += Capture;
				}
			}
			
			EF.NewFilename = StripInvalidCharacters(NewName);
		}

		/// <summary>
		/// Removes invalid characters for a file name, such as \/:*?"<>|
		/// </summary>
		private string StripInvalidCharacters(string Name)
		{
			return Regex.Replace(Name, @"[\\/:\*?""'<>|]", " ");
		}
	}

}


